#ifndef XMLPARSER_UTIL_H_
#define XMLPARSER_UTIL_H_

#ifndef __INCLUDE_XML_NODE__
#  include "xmlParser.h"
#endif

#include <sstream>


namespace XmlParser
{

  //! Thrown when a helper template function encounters an error.
  struct XmlParserError : std::runtime_error {
    XmlParserError(const std::string& s) : std::runtime_error(s) {}
  };


  /////////////////////////////////////////////////
  // GET TEXT
  /////////////////////////////////////////////////

  //! Get the text of the given node as a T, 0 if it does not exist.
  template <typename T>
  inline T get_node_text_val(XMLNode& n, bool throwErrors = true)
  {
    T val = 0;

    if (n.nText() > 0)
    {
      const char* cptxt = n.getText();
      std::string txt(cptxt);
      std::stringstream ss;
      ss << txt;
      ss >> val;
    }
    else if (throwErrors)
    {
      std::stringstream ss;
      const char* nm = n.getName();
      ss << "Attempted to read text value from a tag with no text: " << (nm ? nm : "[noname]");
      throw std::runtime_error(ss.str());
    }
    return val;
  }

  //! Specialization for std::string
  template <>
  inline std::string get_node_text_val(XMLNode& n, bool throwErrors)
  {
    if (n.nText() > 0)
      return std::string(n.getText());
    else if (throwErrors)
    {
      std::stringstream ss;
      const char* nm = n.getName();
      ss << "Attempted to read string text value from a tag with no text: " << (nm ? nm : "[noname]");
      throw XmlParserError(ss.str());
    }
    return std::string();
  }

  //! Read text from node as a string
  inline std::string get_node_text(XMLNode& n) { return get_node_text_val<std::string>(n); }



  /////////////////////////////////////////////////
  // SET TEXT
  /////////////////////////////////////////////////

  //! Sets the text of a node to what stringstream would output to
  template <typename T>
  inline void set_node_text(XMLNode& n, T t)
  {
    std::stringstream ss;
    ss << t;
    std::string str;
    ss >> str;
    n.updateText(str.c_str());
  }

  //! Specialization for std::string
  //want to pass by reference but when I do the above generic version is what gets called :(
  template <>
  inline void set_node_text(XMLNode& n, std::string str)
  {
    n.updateText(str.c_str());
  }

  ////////////////////////////////////////
  // GET ATTRIBUTE
  ////////////////////////////////////////

  //! Get as a T the contents of an attribute
  template <typename T>
  inline T get_node_attribute_val(XMLNode& n, std::string attr_name, bool throwErrors = true)
  {
    T val = 0;

    if (n.nAttribute() > 0 && n.getAttribute(attr_name.c_str()) != NULL)
    {
      const char* cptxt = n.getAttribute(attr_name.c_str());
      std::string txt(cptxt);
      std::stringstream ss;
      ss << txt;
      ss >> val;
      //if (!ss.good() && throwErrors)
        //throw XmlParserError("Unable to properly parse value");
    }
    else if (throwErrors)
    {
      std::stringstream ss;
      const char* nm = n.getName();
      ss << "Failed to read attribute '" << attr_name << "'fromt tag: " << (nm ? nm : "[noname]");
      throw XmlParserError(ss.str());
    }
    return val;
  }

  //! Specialization for std::string
  template <>
  inline std::string get_node_attribute_val(XMLNode& n, std::string attr_name, bool throwErrors)
  {
    if (n.nAttribute() > 0 && n.getAttribute(attr_name.c_str()) != NULL) { 
      const char* c = n.getAttribute(attr_name.c_str());
      return std::string(c);
    }
    else if (throwErrors)
    {
      std::stringstream ss;
      const char* nm = n.getName();
      ss << "Failed to read string attribute '" << attr_name << "'fromt tag: " << (nm ? nm : "[noname]");
      throw XmlParserError(ss.str());
    }
    return std::string();
  }

  //! Specialization for bool
  template <>
  inline bool get_node_attribute_val(XMLNode& n, std::string attr_name, bool throwErrors)
  {
    if (n.nAttribute() > 0 && n.getAttribute(attr_name.c_str()) != NULL) { 
      const char* c = n.getAttribute(attr_name.c_str());
      
      std::string str(c);

      if (str == "true" || str == "True" || str == "TRUE" || str == "1")
      {
        return true;
      }
      else
      {
        return false;
      }
    }
    else if (throwErrors)
    {
      std::stringstream ss;
      const char* nm = n.getName();
      ss << "Failed to read string attribute '" << attr_name << "'fromt tag: " << (nm ? nm : "[noname]");
      throw XmlParserError(ss.str());
    }
    return false;
  }

  //! Get content of attribute as a string
  inline std::string get_node_attribute(XMLNode& n, std::string attr_name) { return get_node_attribute_val<std::string>(n, attr_name); }

  /////////////////////////////////////////////////
  // SET ATTRIBUTE
  /////////////////////////////////////////////////

  //! Sets the text of the attribute to what stringstream would output to
  template <typename T>
  inline void set_node_attribute(XMLNode& n, std::string attr_name, T t)
  {
    std::stringstream ss;
    ss << t;
    std::string str;
    ss >> str;
    n.updateAttribute(str.c_str(), attr_name.c_str(), attr_name.c_str());
  }

  //! Specialize for std::string
  template <>
  inline void set_node_attribute(XMLNode& n, std::string attr_name, std::string str)
  {
    n.updateAttribute(str.c_str(), attr_name.c_str(), attr_name.c_str());
  }
}

#endif